﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace STSdb.MTL
{
    /// <summary>
    /// Part of TransactionUnit class visible to the end user.
    /// MUST BE CALLED ONLY FROM ONE THREAD
    /// </summary>
    public class Transaction : IDisposable
    {
        //STSdb.tiesky.MTL
        /// <summary>
        /// Managed threadId of the transaction
        /// </summary>
        public int ManagedThreadId = 0;

        /// <summary>
        /// Transaction Timeout in seconds, not working for now
        /// </summary>
        public int TransactionTimeout = 0;

        
        private TransactionUnit _transactionUnit = null;

        /// <summary>
        /// Wrapper for calling transaction methods with all parameters as strings.
        /// </summary>
        public TransactionNonGenerics NonGenerics = null;

        /// <summary>
        /// IN DEV. Technical not for user usage.
        /// </summary>
        public TransactionDistributedSupport TransactionDistributedSupport = null;



        public Transaction(TransactionUnit transactionUnit)
        {            
            ManagedThreadId = System.Threading.Thread.CurrentThread.ManagedThreadId;

            _transactionUnit = transactionUnit;

            this.NonGenerics = new TransactionNonGenerics(this);
            this.TransactionDistributedSupport = new TransactionDistributedSupport(this._transactionUnit,this);
        }
        
        public void Dispose()
        {
            //unregistering transaction

            this._transactionUnit.TransactionsCoordinator.UnregisterTransaction(this.ManagedThreadId);
        }
        
         

        #region "MaxRow, MinRow"

        /// <summary>
        /// Returns minimal key from the table.
        /// <para>Can return NULL, if no records found.</para>
        /// <para>Uses READ or READ_SYNCHRO select mode, automatically depending upon transaction</para>
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="T1"></typeparam>
        /// <param name="tableName"></param>
        /// <returns></returns>
        public TableRow<T, T1> MinRow<T, T1>(string tableName)
        {
            if (this._transactionUnit.GetTransactionWriteTablesNames().Contains(tableName))
                return SelectForward<T, T1>(tableName, eSelectMode.READ_SYNCHRO).Take(1).FirstOrDefault();
            else
                return SelectForward<T, T1>(tableName, eSelectMode.READ).Take(1).FirstOrDefault();

        }

        /// <summary>
        /// Returns minimal key from the table.
        /// <para>Can return NULL, if no records found.</para>
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="T1"></typeparam>
        /// <param name="tableName"></param>
        /// <param name="use_READ_SYNCHRO">Reserves the table for the modification if true</param>
        /// <returns></returns>
        public TableRow<T, T1> MinRow<T, T1>(string tableName, bool use_READ_SYNCHRO)
        {
            if (use_READ_SYNCHRO || this._transactionUnit.GetTransactionWriteTablesNames().Contains(tableName))
                return SelectForward<T, T1>(tableName, eSelectMode.READ_SYNCHRO).Take(1).FirstOrDefault();
            else
                return SelectForward<T, T1>(tableName, eSelectMode.READ).Take(1).FirstOrDefault();

        }

        /// <summary>
        /// Returns maximal key from the table.
        /// <para>Can return NULL, if no records found.</para>
        /// <para>Uses READ or READ_SYNCHRO select mode, automatically depending upon transaction</para>
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="T1"></typeparam>
        /// <param name="tableName"></param>
        /// <returns></returns>
        public TableRow<T, T1> MaxRow<T, T1>(string tableName)
        {
            if (this._transactionUnit.GetTransactionWriteTablesNames().Contains(tableName))
                return SelectBackward<T, T1>(tableName, eSelectMode.READ_SYNCHRO).Take(1).FirstOrDefault();
            else
                return SelectBackward<T, T1>(tableName, eSelectMode.READ).Take(1).FirstOrDefault();

        }

 
        /// <summary>
        /// Returns maximal key from the table.
        /// <para>Can return NULL, if no records found.</para>
        /// </summary>
        /// <typeparam name="T">key type of the table</typeparam>
        /// <typeparam name="T1">value type of the table</typeparam>
        /// <param name="tableName"></param>
        /// <param name="use_READ_SYNCHRO">Reserves the table for the modification if true</param>
        /// <returns></returns>
        public TableRow<T, T1> MaxRow<T, T1>(string tableName, bool use_READ_SYNCHRO)
        {            
            if (use_READ_SYNCHRO || this._transactionUnit.GetTransactionWriteTablesNames().Contains(tableName))
                return SelectBackward<T, T1>(tableName, eSelectMode.READ_SYNCHRO).Take(1).FirstOrDefault();
            else
                return SelectBackward<T, T1>(tableName, eSelectMode.READ).Take(1).FirstOrDefault();

        }

        #endregion

        #region "Count"

        /// <summary>
        /// Returns quantity of records inside of the table.
        /// <para>If table is modified in this transaction, then modified count will be returned</para>
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="T1"></typeparam>
        /// <param name="tableName"></param>
        /// <returns></returns>
        public ulong Count<T,T1>(string tableName)
        {
            STSdb.MTL.XTableMTL<T, T1> table = null;

            if (this._transactionUnit.GetTransactionWriteTablesNames().Contains(tableName))
                table = this._transactionUnit.TransactionsCoordinator.Get_Table_Write<T, T1>(tableName, this.ManagedThreadId);
            else
                table = this._transactionUnit.TransactionsCoordinator.Get_Table_Read<T, T1>(tableName, this.ManagedThreadId);

            ulong count = table.Count;
                     

            return count;
        }


        /// <summary>
        /// Returns quantity of records inside of the table.
        /// <para>If table is modified in this transaction, then modified count will be returned</para>
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="T1"></typeparam>
        /// <param name="tableName"></param>
        /// <param name="use_READ_SYNCHRO">Reserves the table for the modification if true</param>
        /// <returns></returns>
        public ulong Count<T, T1>(string tableName, bool use_READ_SYNCHRO)
        {
            //STSdb.Data.XTable<T, T1> table = null;
            STSdb.MTL.XTableMTL<T, T1> table = null;
            
            if (use_READ_SYNCHRO || this._transactionUnit.GetTransactionWriteTablesNames().Contains(tableName))
                table = this._transactionUnit.TransactionsCoordinator.Get_Table_Write<T, T1>(tableName, this.ManagedThreadId);
            else
                table = this._transactionUnit.TransactionsCoordinator.Get_Table_Read<T, T1>(tableName, this.ManagedThreadId);

            ulong count = table.Count;
                      

            return count;
        }     

        #endregion

        #region "Table Exists"

        /// <summary>
        /// Returns true if table Exists.
        /// Can also be accessed via Engine.Scheme
        /// </summary>
        /// <param name="tableName"></param>
        /// <returns></returns>
        public bool IF_TableExists(string tableName)
        {
            return this._transactionUnit.TransactionsCoordinator.SchemaCoordinator.IF_TableExists(tableName);
        }

        #endregion


        #region "Registering Write or Read-Synchro Tables for the transaction before starting any operation for avoiding Deadlocks"

        /// <summary>
        /// <para>Necessary function if it's needed to mutate more then one table in one transaction in multi-threaded environment</para>
        /// Registers tables supposed to be written or read-synchro starting operations, to avoid deadlock situations.
        /// </summary>
        /// <param name="tablesNames"></param>
        public void RegisterWriteOrSynchroReadTables(List<string> tablesNames)
        {
            this.CheckIfTransactionHasTablesRegisteredForWrite(tablesNames);
            this._transactionUnit.TransactionsCoordinator.RegisterWriteTablesForTransaction(this.ManagedThreadId, tablesNames);
        }

        /// <summary>
        /// <para>Necessary function if it's needed to mutate more then one table in one transaction in multi-threaded environment</para>
        /// Registers tables supposed to be written or read-synchro starting operations, to avoid deadlock situations.
        /// </summary>
        /// <param name="tablesNames"></param>
        public void RegisterWriteOrSynchroReadTables(params string[] tablesNames)
        {
            this.RegisterWriteOrSynchroReadTables(tablesNames.Select(r => r).ToList());
        }

        private void CheckIfTransactionHasTablesRegisteredForWrite(List<string> tablesNames)
        {
            if (!this._transactionUnit.TransactionsCoordinator.IfUserStillCanRegisterMutatableTables(this.ManagedThreadId, tablesNames))
                throw new Exception("STSdb.MTL: CAN'T RESERVE TABLES, BECAUSE THERE ARE ALREADY RESERVED TABLES");
        }

        #endregion
        

        #region "INSERT VARIATIONS"

        /// <summary>
        /// Inserts single element into a table. 
        /// <para>Transaction must be commited afterwards.</para>
        /// <para>If table doesn't exist, then it will be created and stored in the schema even if transaction will not be commited.</para>      
        /// </summary>
        /// <typeparam name="T">is a key dataType</typeparam>
        /// <typeparam name="T1">is a value dataType</typeparam>
        /// <param name="tableName">table name as string</param>
        /// <param name="key"></param>
        /// <param name="value"></param>*/
        public void Insert<T, T1>(string tableName, T key, T1 value)
        {
            var table = this._transactionUnit.TransactionsCoordinator.Get_Table_Write<T, T1>(tableName, this.ManagedThreadId);

            table[key] = value;
            /* IF all has to be stored in one rawFile */
            //lock (this._transactionUnit.TransactionsCoordinator.SchemaCoordinator.MainRawFileLock)
            //{
            //    table[key] = value;
            //}

            ///*****************************  Logging transaction element  *****************************/
            //if (this._transactionUnit.TransactionsCoordinator.TransactionsLog != null)
            //{
            //    this._transactionUnit.TransactionsCoordinator.TransactionsLog.Log(
            //        new EngineTransactionsLog.TransactionOperation()
            //        {
            //            TableName = tableName,
            //            ManagedThreadId = this.ManagedThreadId,
            //            TransactionOperationType = EngineTransactionsLog.eTransactionOperationType.INSERT,
            //            Key = key,
            //            Value = value
            //        });
            //}
            ///*****************************  END OF Logging transaction element  *****************************/
        }

        /// <summary>
        /// Inserts elements. Transaction must be commited afterwards.
        /// <para>If table doesn't exist, then it will be created and stored in the schema even if transaction will not be commited.</para>
        /// </summary>
        /// <typeparam name="T">is a key dataType</typeparam>
        /// <typeparam name="T1">is a value dataType</typeparam>
        /// <param name="tableName">table name as string</param>
        /// <param name="collection"></param>
        public void InsertBatch<T, T1>(string tableName, IEnumerable<KeyValuePair<T, T1>> collection)
        {            
            var table = this._transactionUnit.TransactionsCoordinator.Get_Table_Write<T, T1>(tableName, this.ManagedThreadId);

            foreach (var v in collection)
                table[v.Key] = v.Value;

            /* IF all has to be stored in one rawFile */
            //lock (this._transactionUnit.TransactionsCoordinator.SchemaCoordinator.MainRawFileLock)
            //{
            //    foreach (var v in collection)
            //        table[v.Key] = v.Value;
            //}
        }
              


        #endregion


        #region "SELECT variations"

        /// <summary>
        /// Select single row. 
        /// <para>Never returns null, returns row with a searched keyif row is not found.</para>
        /// <para>Transaction must be commited afterwards.</para>
        /// <para>If table doesn't exist, then it will be created and stored in the schema even if transaction will not be commited.</para>
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="T1"></typeparam>
        /// <param name="tableName"></param>
        /// <param name="transactionReadType"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public TableRow<T, T1> Select<T, T1>(string tableName, eSelectMode transactionReadType, T key)
        {

            STSdb.MTL.XTableMTL<T, T1> table = null;

            if (transactionReadType == eSelectMode.READ_SYNCHRO)
            {
                table = this._transactionUnit.TransactionsCoordinator.Get_Table_Write<T, T1>(tableName, this.ManagedThreadId);
            }
            else if (transactionReadType == eSelectMode.READ)
            {
                table = this._transactionUnit.TransactionsCoordinator.Get_Table_Read<T, T1>(tableName, this.ManagedThreadId);
            }

            TableRow<T, T1> ret = null;
                        
            T1 val;
            bool res = table.TryGet(key, out val);

            ret = new TableRow<T, T1>(key, val, tableName, this, res);

            return ret;
        }

        
        /// <summary>
        /// Yield return of inserted values started from the first key.
        /// <para>Can be breaked inside of a loop</para>
        /// <para>Quantity can be limited with .Take(N): SelectForward(..).Take(15)</para>
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="T1"></typeparam>
        /// <param name="tableName"></param>
        /// <param name="transactionReadType"></param>
        /// <returns></returns>
        public IEnumerable<TableRow<T, T1>> SelectForward<T, T1>(string tableName, eSelectMode transactionReadType)
        {

            STSdb.MTL.XTableMTL<T, T1> table = null;

            if (transactionReadType == eSelectMode.READ_SYNCHRO)
            {
                table = this._transactionUnit.TransactionsCoordinator.Get_Table_Write<T, T1>(tableName, this.ManagedThreadId);
            }
            else if (transactionReadType == eSelectMode.READ)
            {
                table = this._transactionUnit.TransactionsCoordinator.Get_Table_Read<T, T1>(tableName, this.ManagedThreadId);
            }

            foreach (var row in table.Forward())
            {
                yield return new TableRow<T, T1>(row.Key, row.Record, tableName, this, true);
            }

        }


        /// <summary>
        /// Yield return of inserted values started from the startKey.
        /// <para>Can be breaked inside of a loop</para>
        /// <para>Quantity can be limited with .Take(N): SelectForward(..).Take(15)</para>
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="T1"></typeparam>
        /// <param name="tableName"></param>
        /// <param name="transactionReadType"></param>
        /// <param name="startKey"></param>
        /// <returns></returns>
        public IEnumerable<TableRow<T, T1>> SelectForward<T, T1>(string tableName, eSelectMode transactionReadType, T startKey)
        {
            STSdb.MTL.XTableMTL<T, T1> table = null;

            if (transactionReadType == eSelectMode.READ_SYNCHRO)
            {
                table = this._transactionUnit.TransactionsCoordinator.Get_Table_Write<T, T1>(tableName, this.ManagedThreadId);
            }
            else if (transactionReadType == eSelectMode.READ)
            {
                table = this._transactionUnit.TransactionsCoordinator.Get_Table_Read<T, T1>(tableName, this.ManagedThreadId);
            }

            foreach (var row in table.Forward(startKey))
            {
                yield return new TableRow<T, T1>(row.Key, row.Record, tableName, this, true);
            }

        }

        /// <summary>
        /// Yield return of inserted values started from the startKey.
        /// <para>Can be breaked inside of a loop</para>
        /// <para>Quantity can be limited with .Take(N): SelectForward(..).Take(15)</para>
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="T1"></typeparam>
        /// <param name="tableName"></param>
        /// <param name="transactionReadType"></param>
        /// <param name="startKey"></param>
        /// <param name="startKeyCompareModifier">Modifier for the start key</param>
        /// <returns></returns>
        public IEnumerable<TableRow<T, T1>> SelectForward<T, T1>(string tableName, eSelectMode transactionReadType, T startKey,eCompareModifierUp startKeyCompareModifier)
        {
            STSdb.MTL.XTableMTL<T, T1> table = null;

            if (transactionReadType == eSelectMode.READ_SYNCHRO)
            {
                table = this._transactionUnit.TransactionsCoordinator.Get_Table_Write<T, T1>(tableName, this.ManagedThreadId);
            }
            else if (transactionReadType == eSelectMode.READ)
            {
                table = this._transactionUnit.TransactionsCoordinator.Get_Table_Read<T, T1>(tableName, this.ManagedThreadId);
            }


            T newStartKey = startKey;
            Data.Row<T, T1> dr = null;
            bool goOn = true;

            if (startKeyCompareModifier == eCompareModifierUp.More)
            {
                dr = table.FindAfter(startKey);
                if (dr == null)
                    goOn = false;
                else
                    newStartKey = dr.Key;
            }

            if (goOn)
            {
                foreach (var row in table.Forward(newStartKey))
                {
                    yield return new TableRow<T, T1>(row.Key, row.Record, tableName, this, true);
                }
            }           

        }

        
        /// <summary>
        /// Yield return of inserted values started from the startKey and ended with endKey.
        /// <para>Can be breaked inside of a loop</para>
        /// <para>Quantity can be limited with .Take(N): SelectForward(..).Take(15)</para> 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="T1"></typeparam>
        /// <param name="tableName"></param>
        /// <param name="transactionReadType"></param>
        /// <param name="startKey"></param>
        /// <param name="startKeyCompareModifier">Modifier for the start key</param>
        /// <param name="endKey"></param>
        /// <param name="endKeyCompareModifier">Modifier for the end key</param>
        /// <returns></returns>
        public IEnumerable<TableRow<T, T1>> SelectForward<T, T1>(string tableName, eSelectMode transactionReadType, T startKey,eCompareModifierUp startKeyCompareModifier, T endKey,eCompareModifierDown endKeyCompareModifier)
        {
            STSdb.MTL.XTableMTL<T, T1> table = null;

            if (transactionReadType == eSelectMode.READ_SYNCHRO)
            {
                table = this._transactionUnit.TransactionsCoordinator.Get_Table_Write<T, T1>(tableName, this.ManagedThreadId);
            }
            else if (transactionReadType == eSelectMode.READ)
            {
                table = this._transactionUnit.TransactionsCoordinator.Get_Table_Read<T, T1>(tableName, this.ManagedThreadId);
            }

            T newStartKey = startKey;
            T newEndKey = endKey;
            Data.Row<T, T1> dr = null;
            bool goOn = true;

            if (startKeyCompareModifier == eCompareModifierUp.More)
            {
                dr = table.FindAfter(startKey);
                if (dr == null)
                    goOn = false;
                else
                    newStartKey = dr.Key;
            }

            if (goOn && endKeyCompareModifier == eCompareModifierDown.Less)
            {
                
                dr = table.FindBefore(endKey);                
                if (dr == null)
                    goOn = false;
                else
                    newEndKey = dr.Key;
            }

            if (goOn)
            {
                foreach (var row in table.Forward(newStartKey, newEndKey))
                {
                    yield return new TableRow<T, T1>(row.Key, row.Record, tableName, this, true);
                }
            }
        }

        /// <summary>
        /// Yield return of inserted values started from the startKey and ended with endKey.
        /// <para>Can be breaked inside of a loop</para>
        /// <para>Quantity can be limited with .Take(N): SelectForward(..).Take(15)</para>
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="T1"></typeparam>
        /// <param name="tableName"></param>
        /// <param name="transactionReadType"></param>
        /// <param name="startKey"></param>
        /// <param name="endKey"></param>
        /// <returns></returns>
        public IEnumerable<TableRow<T, T1>> SelectForward<T, T1>(string tableName, eSelectMode transactionReadType, T startKey, T endKey)
        {
            STSdb.MTL.XTableMTL<T, T1> table = null;

            if (transactionReadType == eSelectMode.READ_SYNCHRO)
            {
                table = this._transactionUnit.TransactionsCoordinator.Get_Table_Write<T, T1>(tableName, this.ManagedThreadId);
            }
            else if (transactionReadType == eSelectMode.READ)
            {
                table = this._transactionUnit.TransactionsCoordinator.Get_Table_Read<T, T1>(tableName, this.ManagedThreadId);
            }

            foreach (var row in table.Forward(startKey, endKey))
            {
                yield return new TableRow<T, T1>(row.Key, row.Record, tableName, this, true);
            }

        }


        /// <summary>
        /// Yield return of inserted values started from the last inserted key and backward.
        /// <para>Can be breaked inside of a loop</para>
        /// <para>Quantity can be limited with .Take(N): SelectBackward(..).Take(15)</para>
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="T1"></typeparam>
        /// <param name="tableName"></param>
        /// <param name="transactionReadType"></param>
        /// <returns></returns>
        public IEnumerable<TableRow<T, T1>> SelectBackward<T, T1>(string tableName, eSelectMode transactionReadType)
        {
            STSdb.MTL.XTableMTL<T, T1> table = null;

            if (transactionReadType == eSelectMode.READ_SYNCHRO)
            {
                table = this._transactionUnit.TransactionsCoordinator.Get_Table_Write<T, T1>(tableName, this.ManagedThreadId);
            }
            else if (transactionReadType == eSelectMode.READ)
            {
                table = this._transactionUnit.TransactionsCoordinator.Get_Table_Read<T, T1>(tableName, this.ManagedThreadId);
            }

            foreach (var row in table.Backward())
            {
                yield return new TableRow<T, T1>(row.Key, row.Record, tableName, this, true);
            }

        }

        /// <summary>
        /// Yield return of inserted values started from the lastKey and backward.
        /// <para>Can be breaked inside of a loop</para>
        /// <para>Quantity can be limited with .Take(N): SelectBackward(..).Take(15)</para>
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="T1"></typeparam>
        /// <param name="tableName"></param>
        /// <param name="transactionReadType"></param>
        /// <param name="lastKey"></param>
        /// <returns></returns>
        public IEnumerable<TableRow<T, T1>> SelectBackward<T, T1>(string tableName, eSelectMode transactionReadType, T lastKey)
        {
            STSdb.MTL.XTableMTL<T, T1> table = null;

            if (transactionReadType == eSelectMode.READ_SYNCHRO)
            {
                table = this._transactionUnit.TransactionsCoordinator.Get_Table_Write<T, T1>(tableName, this.ManagedThreadId);
            }
            else if (transactionReadType == eSelectMode.READ)
            {
                table = this._transactionUnit.TransactionsCoordinator.Get_Table_Read<T, T1>(tableName, this.ManagedThreadId);
            }

            foreach (var row in table.Backward(lastKey))
            {
                yield return new TableRow<T, T1>(row.Key, row.Record, tableName, this, true);
            }

        }


        /// <summary>
        /// Yield return of inserted values started from the lastKey and backward.
        /// <para>Can be breaked inside of a loop</para>
        /// <para>Quantity can be limited with .Take(N): SelectBackward(..).Take(15)</para> 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="T1"></typeparam>
        /// <param name="tableName"></param>
        /// <param name="transactionReadType"></param>
        /// <param name="lastKey"></param>
        /// <param name="lastKeyCompareModifier">Last Key compare modifier</param>
        /// <returns></returns>
        public IEnumerable<TableRow<T, T1>> SelectBackward<T, T1>(string tableName, eSelectMode transactionReadType, T lastKey, eCompareModifierDown lastKeyCompareModifier)
        {
            STSdb.MTL.XTableMTL<T, T1> table = null;

            if (transactionReadType == eSelectMode.READ_SYNCHRO)
            {
                table = this._transactionUnit.TransactionsCoordinator.Get_Table_Write<T, T1>(tableName, this.ManagedThreadId);
            }
            else if (transactionReadType == eSelectMode.READ)
            {
                table = this._transactionUnit.TransactionsCoordinator.Get_Table_Read<T, T1>(tableName, this.ManagedThreadId);
            }

            T newLastKey = lastKey;
            Data.Row<T, T1> dr = null;
            bool goOn = true;

            if (lastKeyCompareModifier == eCompareModifierDown.Less)
            {
                dr = table.FindBefore(lastKey);
                if (dr == null)
                    goOn = false;
                else
                    newLastKey = dr.Key;
            }

            if (goOn)
            {
                foreach (var row in table.Backward(newLastKey))
                {
                    yield return new TableRow<T, T1>(row.Key, row.Record, tableName, this, true);
                }
            }

        }

        /// <summary>
        /// Yield return of inserted values started from the lastKey and backward till the firstKey.
        /// <para>Can be breaked inside of a loop</para>
        /// <para>Quantity can be limited with .Take(N): SelectBackward(..).Take(15)</para>
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="T1"></typeparam>
        /// <param name="tableName"></param>
        /// <param name="transactionReadType"></param>
        /// <param name="lastKey"></param>
        /// <param name="firstKey"></param>
        /// <returns></returns>
        public IEnumerable<TableRow<T, T1>> SelectBackward<T, T1>(string tableName, eSelectMode transactionReadType, T lastKey, T firstKey)
        {
            STSdb.MTL.XTableMTL<T, T1> table = null;

            if (transactionReadType == eSelectMode.READ_SYNCHRO)
            {
                table = this._transactionUnit.TransactionsCoordinator.Get_Table_Write<T, T1>(tableName, this.ManagedThreadId);
            }
            else if (transactionReadType == eSelectMode.READ)
            {
                table = this._transactionUnit.TransactionsCoordinator.Get_Table_Read<T, T1>(tableName, this.ManagedThreadId);
            }

            foreach (var row in table.Backward(lastKey, firstKey))
            {
                yield return new TableRow<T, T1>(row.Key, row.Record, tableName, this, true);
            }

        }


        /// <summary>
        /// Yield return of inserted values started from the lastKey and backward till the firstKey.
        /// <para>Can be breaked inside of a loop</para>
        /// <para>Quantity can be limited with .Take(N): SelectBackward(..).Take(15)</para>
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="T1"></typeparam>
        /// <param name="tableName"></param>
        /// <param name="transactionReadType"></param>
        /// <param name="lastKey"></param>
        /// <param name="lastKeyCompareModifier">Last Key compare modifier</param>
        /// <param name="firstKey"></param>
        /// <param name="firstKeyCompareModifier">First Key compare modifier</param>
        /// <returns></returns>
        public IEnumerable<TableRow<T, T1>> SelectBackward<T, T1>(string tableName, eSelectMode transactionReadType, T lastKey, eCompareModifierDown lastKeyCompareModifier, T firstKey, eCompareModifierUp firstKeyCompareModifier)
        {
            STSdb.MTL.XTableMTL<T, T1> table = null;

            if (transactionReadType == eSelectMode.READ_SYNCHRO)
            {
                table = this._transactionUnit.TransactionsCoordinator.Get_Table_Write<T, T1>(tableName, this.ManagedThreadId);
            }
            else if (transactionReadType == eSelectMode.READ)
            {
                table = this._transactionUnit.TransactionsCoordinator.Get_Table_Read<T, T1>(tableName, this.ManagedThreadId);
            }

            T newLastKey = lastKey;
            T newFirstKey = firstKey;
            Data.Row<T, T1> dr = null;
            bool goOn = true;

            if (lastKeyCompareModifier == eCompareModifierDown.Less)
            {
                dr = table.FindBefore(lastKey);
                if (dr == null)
                    goOn = false;
                else
                    newLastKey = dr.Key;
            }

            if (firstKeyCompareModifier == eCompareModifierUp.More)
            {
                dr = table.FindAfter(firstKey);
                if (dr == null)
                    goOn = false;
                else
                    newFirstKey = dr.Key;
            }

            if (goOn)
            {
                foreach (var row in table.Backward(newLastKey, newFirstKey))
                {
                    yield return new TableRow<T, T1>(row.Key, row.Record, tableName, this, true);
                }
            }

            

        }
        #endregion


        #region "UPDATE variations"

        /// <summary>
        /// Updates value for the specific key.
        /// <para>Insert method can be used with the same semantic.</para>
        /// <para>If we got in our transaction STSdb.MTL.TableRow tr=...; then tr.Key=new key or tr.Value=new value, can be used with the same semantic</para>
        /// <para>Transaction must be commited afterwards.</para>
        /// <para>If table doesn't exist, then it will be created and stored in the schema even if transaction will not be commited.</para>
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="T1"></typeparam>
        /// <param name="tableName"></param>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public void UpdateValue<T, T1>(string tableName, T key, T1 value)
        {

            STSdb.MTL.XTableMTL<T, T1> table = this._transactionUnit.TransactionsCoordinator.Get_Table_Write<T, T1>(tableName, this.ManagedThreadId);

            table[key] = value;

            /* IF all has to be stored in one rawFile */
            //lock (this._transactionUnit.TransactionsCoordinator.SchemaCoordinator.MainRawFileLock)
            //{
            //    table[key] = value;
            //}
        }

        /// <summary>
        /// Update values for the specified keys.
        /// <para>InsertBatch can be used with the same semantic</para>
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="T1"></typeparam>
        /// <param name="tableName"></param>
        /// <param name="collection"></param>
        public void UpdateValues<T, T1>(string tableName, IEnumerable<KeyValuePair<T, T1>> collection)
        {

            STSdb.MTL.XTableMTL<T, T1> table = this._transactionUnit.TransactionsCoordinator.Get_Table_Write<T, T1>(tableName, this.ManagedThreadId);


            foreach (var e in collection)
            {
                table[e.Key] = e.Value;
            }

            /* IF all has to be stored in one rawFile */
            //lock (this._transactionUnit.TransactionsCoordinator.SchemaCoordinator.MainRawFileLock)
            //{
            //    foreach (var e in collection)
            //    {
            //        table[e.Key] = e.Value;
            //    }
            //}
        }

        /// <summary>
        /// Sets a new key for the old key.
        /// <para>If we got in our transaction STSdb.MTL.TableRow tr=...; then tr.Key=new key or tr.Value=new value, can be used with the same semantic</para>
        /// <para>Transaction must be commited afterwards.</para>
        /// <para>If table doesn't exist, then it will be created and stored in the schema even if transaction will not be commited.</para>
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="T1"></typeparam>
        /// <param name="tableName"></param>
        /// <param name="key"></param>
        /// <param name="newKey"></param>
        public void UpdateKey<T,T1>(string tableName, T key, T newKey)
        {
            STSdb.MTL.XTableMTL<T, T1> table = this._transactionUnit.TransactionsCoordinator.Get_Table_Write<T, T1>(tableName, this.ManagedThreadId);

            table.Change(key, newKey);

            /* IF all has to be stored in one rawFile */
            //lock (this._transactionUnit.TransactionsCoordinator.SchemaCoordinator.MainRawFileLock)
            //{
            //    table.Change(key, newKey);
            //}
        }

        /// <summary>
        /// Sets new keys for old keys
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="T1"></typeparam>
        /// <param name="tableName"></param>
        /// <param name="collection"></param>
        public void UpdateKeys<T, T1>(string tableName, IEnumerable<KeyValuePair<T, T>> collection)
        {
            STSdb.MTL.XTableMTL<T, T1> table = this._transactionUnit.TransactionsCoordinator.Get_Table_Write<T, T1>(tableName, this.ManagedThreadId);
            foreach (var e in collection)
            {

                table.Change(e.Key, e.Value);

                /* IF all has to be stored in one rawFile */
                //lock (this._transactionUnit.TransactionsCoordinator.SchemaCoordinator.MainRawFileLock)
                //{
                //    table.Change(e.Key, e.Value);
                //}
            }
        }

        // IEnumerable<KeyValuePair<T, T1>> collection
        
        #endregion


        #region "DELETE varaiations"

        /// <summary>
        /// Deletes a single key from a table
        /// <para>Transaction must be commited afterwards.</para>
        /// <para>If table doesn't exist, then it will be created and stored in the schema even if transaction will not be commited.</para>
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="T1"></typeparam>
        /// <param name="tableName"></param>
        /// <param name="key"></param>
        public void Delete<T, T1>(string tableName, T key)
        {

            STSdb.MTL.XTableMTL<T, T1> table = this._transactionUnit.TransactionsCoordinator.Get_Table_Write<T, T1>(tableName, this.ManagedThreadId);

            table.Delete(key);

            /* IF all has to be stored in one rawFile */
            //lock (this._transactionUnit.TransactionsCoordinator.SchemaCoordinator.MainRawFileLock)
            //{
            //    table.Delete(key);
            //}
        }

      
        /// <summary>
        /// Deletes range of keys from a table
        /// <para>Transaction must be commited afterwards.</para>
        /// <para>If table doesn't exist, then it will be created and stored in the schema even if transaction will not be commited.</para>
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="T1"></typeparam>
        /// <param name="tableName"></param>
        /// <param name="firstKey">from key</param>
        /// <param name="lastKey">to key</param>
        public void Delete<T, T1>(string tableName, T firstKey,T lastKey)
        {

            STSdb.MTL.XTableMTL<T, T1> table = this._transactionUnit.TransactionsCoordinator.Get_Table_Write<T, T1>(tableName, this.ManagedThreadId);

            table.Delete(firstKey, lastKey);

            /* IF all has to be stored in one rawFile */
            //lock (this._transactionUnit.TransactionsCoordinator.SchemaCoordinator.MainRawFileLock)
            //{
            //    table.Delete(firstKey, lastKey);
            //}
        }

        /// <summary>
        /// Deletes keys supplied by the keys collection
        /// <para>Transaction must be commited afterwards.</para>
        /// <para>If table doesn't exist, then it will be created and stored in the schema even if transaction will not be commited.</para>
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="T1"></typeparam>
        /// <param name="tableName"></param>
        /// <param name="key">Keys to be deleted</param>
        public void Delete<T, T1>(string tableName, IEnumerable<T> keys)
        {

            STSdb.MTL.XTableMTL<T, T1> table = this._transactionUnit.TransactionsCoordinator.Get_Table_Write<T, T1>(tableName, this.ManagedThreadId);

            foreach (var key in keys)
                table.Delete(key);

            /* IF all has to be stored in one rawFile */
            //lock (this._transactionUnit.TransactionsCoordinator.SchemaCoordinator.MainRawFileLock)
            //{
            //    foreach (var key in keys)
            //        table.Delete(key);
            //}
        }

        /// <summary>
        /// Delete all records from the table
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="T1"></typeparam>
        /// <param name="tableName"></param>
        public void DeleteAll<T, T1>(string tableName)
        {
            STSdb.MTL.XTableMTL<T, T1> table = this._transactionUnit.TransactionsCoordinator.Get_Table_Write<T, T1>(tableName, this.ManagedThreadId);

            table.Clear();

            /* IF all has to be stored in one rawFile */
            //lock (this._transactionUnit.TransactionsCoordinator.SchemaCoordinator.MainRawFileLock)
            //{
            //    table.Clear();
            //}
        }

        #endregion


        #region "Delete Tables"

        /// <summary>
        /// Deletes table from database schema and from internal storage structures.
        /// <para>Parameter deleteAlsoRawFile gives ability to delete also raw file where XTable is situated and release space for VFS garbage collector</para>
        /// </summary>
        /// <param name="tableName"></param>
        /// <param name="deleteAlsoRawFile"></param>
        public void DeleteTable(string tableName, bool deleteAlsoRawFile)
        {
            if (deleteAlsoRawFile)
            {
                this._transactionUnit.TransactionsCoordinator.SchemaCoordinator.DeleteTableWithRawFile(tableName, deleteAlsoRawFile);
            }
            else
            {
                this._transactionUnit.TransactionsCoordinator.SchemaCoordinator.DeleteTable(tableName);
            }
        }

        /// <summary>
        /// Delete tables from database schema and from internal storage structures.
        /// <para>Parameter deleteAlsoRawFile gives ability to delete also raw file where XTable is situated and release space for VFS garbage collector</para>
        /// </summary>
        /// <param name="tableNames"></param>
        /// <param name="deleteAlsoRawFile"></param>
        public void DeleteTables(List<string> tableNames, bool deleteAlsoRawFile)
        {
            if (deleteAlsoRawFile)
            {
                tableNames
                 .ForEach
                 (r =>
                     this._transactionUnit.TransactionsCoordinator.SchemaCoordinator.DeleteTableWithRawFile(r, deleteAlsoRawFile)
                 );
            }
            else
            {
                tableNames
                 .ForEach
                 (r =>
                     this._transactionUnit.TransactionsCoordinator.SchemaCoordinator.DeleteTable(r)
                 );
            }

            
        }

        #endregion


        #region "COMMIT AND ROLLBACK"

        /// <summary>
        /// Commits alls actions inside of transaction
        /// </summary>
        public void Commit()
        {

            /////////////  LOG COMMIT START, when tran has to be committed it also commits in one transaction @sys table who can set value that tran is finished. 
            //For later logging. Exactly logged data can be splitted on daily basis and backed up, then will be possible to restore data from it.


            this._transactionUnit.TransactionsCoordinator.Commit(this.ManagedThreadId,false);


            

            ///*****************************  Logging transaction element  *****************************/
            //if (this._transactionUnit.TransactionsCoordinator.TransactionsLog != null)
            //{
            //    this._transactionUnit.TransactionsCoordinator.TransactionsLog.Log(
            //        new EngineTransactionsLog.TransactionOperation()
            //        {                       
            //            ManagedThreadId = this.ManagedThreadId,
            //            TransactionOperationType = EngineTransactionsLog.eTransactionOperationType.COMMIT,
            //        });
            //}
            ///*****************************  END OF Logging transaction element  *****************************/
        }

        public void Commit(bool ignoreTransaction)
        {
            this._transactionUnit.TransactionsCoordinator.Commit(this.ManagedThreadId, ignoreTransaction);
        }

        /// <summary>
        /// Rolls back all tables changing actions after last commit or from the beginning of the transaction.
        /// </summary>
        public void RollBack()
        {
            //this._transactionUnit.TransactionsCoordinator.RollBack(this.ManagedThreadId);
        }
        #endregion


        #region "Snapshots"

        //NOT AVAILABLE ANY MORE, DUE TO THE COMPACTING OF THE TABLES MECHANISM. BUT INSIDE OF TRANSACTION, THERE ARE ALWAYS AVAILABLE 2 LOCATORS ONE WRITE  or READ_SYNCHRO, 
        //SECOND READ, WHICH GIVES PROJECTION ON THE DATA BEFORE COMMIT. Probably later we can organize inside of one transaction, Commit points and then rollbacks to any of this points.

        ///// <summary>
        ///// IN DEV, must be solved some problems with compacting data
        ///// Creates table snapshot
        ///// </summary>
        ///// <typeparam name="T"></typeparam>
        ///// <typeparam name="T1"></typeparam>
        ///// <param name="tableName"></param>
        ///// <param name="snapshotName"></param>
        //public void CreateTableSnapshot<T,T1>(string tableName, string snapshotName)
        //{
        //    STSdb.Data.XTable<T, T1> table = this._transactionUnit.TransactionsCoordinator.Get_Table_Write<T, T1>(tableName, this.ManagedThreadId);
        //    this._transactionUnit.TransactionsCoordinator.SchemaCoordinator.RegisterSnapshotTable<T, T1>(new Data.Locator(snapshotName), table.Snapshot());

        //}

        ///// <summary>
        ///// IN DEV, must be solved some problems with compacting data
        ///// Restores existing table from the snapshot
        ///// </summary>
        ///// <typeparam name="T"></typeparam>
        ///// <typeparam name="T1"></typeparam>
        ///// <param name="tableName"></param>
        ///// <param name="snapshotName"></param>
        //public void RestoreTableFromSnapshot<T,T1>(string tableName, string snapshotName)
        //{
        //    STSdb.Data.XTable<T, T1> table = this._transactionUnit.TransactionsCoordinator.Get_Table_Write<T, T1>(tableName, this.ManagedThreadId);
        //    ITransactable  itr = this._transactionUnit.GetTransactionWriteTable(tableName);
        //    this._transactionUnit.TransactionsCoordinator.SchemaCoordinator.RestoreTableFromSnapshot(tableName, snapshotName);
        //    itr.InitCreateLocators();
        //}

        #endregion

        #region "GetTablesInfo"

        /// <summary>
        /// Returns all tables names registered in current database.
        /// </summary>
        /// <returns></returns>
        public List<string> GetAllTablesNames()
        {
            return this._transactionUnit.TransactionsCoordinator.SchemaCoordinator.GetAllTablesNames();
        }

        #endregion

    }
}
